package com.bjsxt.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bjsxt.domain.Account;
import com.bjsxt.domain.AccountDetail;
import com.bjsxt.domain.Coin;
import com.bjsxt.domain.Config;
import com.bjsxt.mapper.AccountMapper;
import com.bjsxt.modle.AccountVo;
import com.bjsxt.service.AccountDetailService;
import com.bjsxt.service.AccountService;
import com.bjsxt.service.CoinService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.math.BigDecimal;

@Service
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService {
    /**
     * 币种业务层对象
     */
    @Autowired
    private CoinService coinService;
    /**
     * 平台参数配置数据业务层
     */
    @Autowired
    private ConfigServiceImpl configService;
    /**
     * 资金账户流水业务层
     */
    @Autowired
    private AccountDetailService accountDetailService;

    /**
     * 根据币种 id 和用户 id 查询用户财产记录信息
     *
     * @param coinId 币种 id
     * @param userId 用户 id
     * @return 财产记录
     */
    @Override
    public Account getAccountByCoinIdAndUserId(Long coinId, Long userId) {
        // 根据条件获取数据
        return getOne(new LambdaQueryWrapper<Account>().eq(Account::getCoinId, coinId).eq(Account::getUserId, userId).eq(Account::getStatus, 1));
    }

    /**
     * 根据用户 id 和币种名称查询用户的账户余额
     *
     * @param userId   用户 id
     * @param coinName 币种名称
     * @return 财产记录信息
     */
    @Override
    public AccountVo getByUserIdAndCoinName(Long userId, String coinName) {
        // 根据币种名称查询币种数据
        Coin coin = coinService.getByCoinName(coinName);
        // 断言，如果这个查询的数据为空则返回报错信息
        Assert.notNull(coin, "币种不存在");
        // 获取账户余额数据
        Account account = getOne(new LambdaQueryWrapper<Account>().eq(Account::getUserId, userId).eq(Account::getCoinId, coin.getId()));
        // 断言，如果这个查询的数据为空则返回报错信息
        Assert.notNull(coin, "资产不存在");
        // 查询买入价格需要的费率
        Config buyRateConfig = configService.getByCode("USDT2CNY");
        // 查询卖出价格需要的费率
        Config sellRateConfig = configService.getByCode("CNY2USDT");
        // 创建需要返回的对象
        AccountVo accountVo = new AccountVo();
        BeanUtil.copyProperties(account, accountVo);
        accountVo.setBuyRate(new BigDecimal(buyRateConfig.getValue()));
        accountVo.setSellRate(new BigDecimal(sellRateConfig.getValue()));
        // 返回构建的对象
        return accountVo;
    }

    /**
     * 进行资金的流转
     *
     * @param userId         付款方用户 id
     * @param toUserId       收款方用户 id
     * @param cashRechargeId 订单 ID
     * @param coinId         转账的币种 id
     * @param num            转账金额
     * @param fee            手续费
     * @param direction      入账为1，出账为2
     * @param businessType   业务类型（具体参数请查看文档）
     * @param remark         流水状态：,充值,提现,冻结,解冻,转出,转入
     * @return true 转账成功 false 转账失败
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean accountTransfer(Long userId, Long toUserId, Long cashRechargeId, Long coinId, BigDecimal num, BigDecimal fee, Byte direction, String businessType, String remark) {
        // 获取当前转账源账户的数据
        Account account = getAccountByCoinIdAndUserId(coinId, userId);
        Assert.notNull(account, "用户当前没有这个该币种数据");
        // 增加一条充值流水
        AccountDetail accountDetail = new AccountDetail();
        accountDetail.setUserId(toUserId);
        accountDetail.setCoinId(coinId);
        accountDetail.setAccountId(userId);
        accountDetail.setRefAccountId(userId);
        accountDetail.setOrderId(cashRechargeId);
        accountDetail.setDirection(direction);
        accountDetail.setBusinessType(businessType);
        accountDetail.setAmount(num);
        accountDetail.setFee(fee);
        accountDetail.setRemark(remark);
        // 调用方法进行数据的保存
        boolean flag = save(account);
        Assert.isTrue(flag, "保存流水记录失败");
        switch (direction) {
            case 1: // 入账，增加金钱
                // 进行账户的收款方账户数据增加操作
                account.setBalanceAmount(account.getBalanceAmount().add(num));
                break;
            case 2: // 出账，减少金钱
                if (num.compareTo(account.getBalanceAmount()) > 0)
                    throw new IllegalArgumentException("账户余额不足无法进行减少");
                account.setBalanceAmount(account.getBalanceAmount().subtract(num));
            default:
                throw new IllegalArgumentException("无效的 direction 类型，请检查参数");
        }
        // 调用方法进行数据的保存操作
        return updateById(account);
    }

    /**
     * 扣减锁定账户的余额
     *
     * @param userId  用户 id
     * @param coinId  币种 id
     * @param mum     扣减的金额
     * @param fee     手续费
     * @param orderId 流水编号
     * @param type    类型
     * @return true 扣减成功 false 扣减失败
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deductionAmount(Long userId, Long coinId, BigDecimal mum, BigDecimal fee, Long orderId, String type) {
        // 查询当前需要扣减的账户
        Account account = getOne(new LambdaQueryWrapper<Account>().eq(Account::getUserId, userId).eq(Account::getCoinId, coinId));
        // 判断账户是否存在
        if (account == null) throw new IllegalArgumentException("您的资产不存在");
        if (mum.compareTo(account.getBalanceAmount()) > 0)
            throw new IllegalArgumentException("您的资产不足进行提现，请核对后提现");
        // 进行数据的扣减操作
        account.setBalanceAmount(account.getBalanceAmount().subtract(mum));
        account.setFreezeAmount(account.getFreezeAmount().add(mum));
        // 进行数据的更新
        boolean flag = updateById(account);
        if (!flag) throw new RuntimeException("进行账户的数据扣减失败");
        // 增加账户资产扣除的流水记录，这里的 refAccountId 该订单邀请奖励有这个数据否则一律都是 orderId
        AccountDetail accountDetail = new AccountDetail(null, userId, coinId, account.getId(), account.getId(), orderId, (byte) 2, type, mum, fee, "提现", null);
        // 调用业务层进行数据的保存
        return accountDetailService.save(accountDetail);
    }
}
